/**
 * 地形演员类
 */
 import * as THREE from "./three.js/build/three.module.js";

 import A3Actor from "./A3Actor.js";
import { isNull, createTerrainShape, createRigidBody } from "./A3Common.js";
import { AmmoLib } from "./A3Class.js";

// 生成正弦/余弦地形数据
function generateSinusoidalWave( width, depth, minHeight, maxHeight, type="sin" ) {
  // Generates the height data 

  const size = width * depth;
  const data = new Float32Array( size );

  const hRange = maxHeight - minHeight;
  const w2 = width / 2;
  const d2 = depth / 2;
  const phaseMult = 12;

  let p = 0;

  for ( let j = 0; j < depth; j ++ ) {

    for ( let i = 0; i < width; i ++ ) {

      const radius = Math.sqrt(
        Math.pow( ( i - w2 ) / w2, 2.0 ) +
        Math.pow( ( j - d2 ) / d2, 2.0 ) );
      
      let height;

      switch( type ) {
        case "sin":
          height = ( Math.sin( radius * phaseMult ) + 1 ) * 0.5 * hRange + minHeight;
          break;
        case "cos":
          height = ( Math.cos( radius * phaseMult ) + 1 ) * 0.5 * hRange + minHeight;
          break;
        default:
          height = 0;
          break; 
      }

      data[ p ] = height;

      p ++;

    }

  }

  return data;
}

class A3Terrain extends A3Actor{
  name = "terrain";
  
  terrainScale = 1;
  minHeight = 0;
  maxHeight = 0;

  constructor( widthExtents, depthExtents, width, depth, heightData=null, minHeight, maxHeight ) {

    super();

    if( isNull( width ) ) {
      width = widthExtents;
    }

    if( isNull( depth ) ) {
      depth = depthExtents;
    }

    const geometry = new THREE.PlaneGeometry( 
      widthExtents, 
      depthExtents,
      width - 1,
      depth - 1
    );

    geometry.rotateX( -Math.PI / 2 );

    // 测试地形
    const findMin = isNull( minHeight );
    const findMax = isNull( maxHeight );

    if( ( findMin || findMax ) && !isNull( heightData ) ) {
      minHeight = findMin ? Number.MAX_VALUE : minHeight;
      maxHeight = findMax ? Number.MIN_VALUE : maxHeight;

      for(var i = 0, il = heightData.length; i < il; i++) {
        if(findMin) {
          minHeight = Math.min( minHeight, heightData[i] );
        }

        if(findMax) {
          maxHeight = Math.max( maxHeight, heightData[i] );
        }
      }
    }

    if( !isNull( heightData ) ) {

      const vertices = geometry.attributes.position.array;

      for ( let i = 0, j = 0, l = vertices.length; i < l; i ++, j += 3 ) {

        // j + 1 修改Y值
        vertices[ j + 1 ] = heightData[ i ];

      }

      geometry.computeVertexNormals();

      const terrainMaterial = new THREE.MeshPhongMaterial( { color: 0x0ccc0c } );
      const terrainMesh = new THREE.Mesh( geometry, terrainMaterial );
    
      const terrainShape = createTerrainShape( 
        widthExtents, depthExtents, 
        width, depth, 
        minHeight, maxHeight,
        heightData 
      );

      this.threeObject = terrainMesh;
      this.physicsBody = createRigidBody( terrainShape, 0, this.position, this.quaternion, this.inertia );
      
      this.minHeight = minHeight;
      this.maxHeight = maxHeight;

      this.heightData = heightData;
    }

    this.width = width;
    this.depth = depth;

    this.needsUpdate = true;
  }

  static waveTerrain( width, depth, minHeight, maxHeight, scale=.5, type="sin" ) {
    
    const widthExtents = ( width - 1 ) * scale;
    const depthExtents = ( depth - 1 ) * scale;

    const heightData = generateSinusoidalWave( width, depth, minHeight, maxHeight, type );

    return new A3Terrain( widthExtents, depthExtents, width, depth, heightData );

  }

  setTexture( textureFile="textures/grid.png" ) {
  
    if( !isNull( this.threeObject ) ) {
      this.textureLoader.load( textureFile, ( texture ) => {

        texture.wrapS = THREE.RepeatWrapping;
        texture.wrapT = THREE.RepeatWrapping;
        texture.repeat.set( this.width - 1, this.depth - 1 );
        this.threeObject.material.map = texture;
        this.threeObject.material.needsUpdate = true;

      } );
    }

  }

  // 仅在设置位姿时更新
  update() {
    if( isNull( this.physicsBody ) || isNull( this.threeObject ) ) return;

    const worldTransform = this.worldTransform;
    const ms = this.physicsBody.getMotionState();
    
    if ( ms ) {

      const tmpPos = new THREE.Vector3( 0, (this.maxHeight + this.minHeight) / 2, 0 );

      tmpPos.applyQuaternion( this.quaternion );
      tmpPos.add( this.position );

      if( this.needsUpdate ) {
        
        worldTransform.setOrigin( new AmmoLib.btVector3( 
          tmpPos.x, 
          tmpPos.y, 
          tmpPos.z 
        ) );

        worldTransform.setRotation( new AmmoLib.btQuaternion( this.quaternion.x, this.quaternion.y, this.quaternion.z, this.quaternion.w ) );
        
        this.physicsBody.setWorldTransform( worldTransform );
        this.threeObject.position.copy( this.position );
        this.threeObject.quaternion.copy( this.quaternion );

        this.needsUpdate = false;

      }

    }
  }

 }

 export default A3Terrain;